-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.


local singleton = require 'mc.singleton'
local c_array = require 'array.array_object'
local class = require 'mc.class'
local class_mgmt = require 'mc.class_mgnt'
local method_misc = require 'method_misc'
local signal = require 'mc.signal'

local DISK_ARRAY_NAME <const> = 'DiskArray'

local c_array_collection = class()

function c_array_collection:ctor(storage_app_service)
    self.array_list = {}
    self.storage_app_service = storage_app_service
    self.object_manager = storage_app_service.object_manager
    self.on_add_array = signal.new()
    self.on_del_array = signal.new()
    self.on_update_drive_list = signal.new()
    self.on_controller_commu_changed = signal.new()
end

function c_array_collection:init()
    self.on_add_array:on(function(controller_id, controller_path, array_id)
        -- 在资源树上创建对象
        local obj = self.storage_app_service:CreateDiskArray(1, controller_path, array_id)
        self.array_list[string.format('%d:%d', controller_id, array_id)] = obj
        obj.Id = array_id
        -- 创建受模块管理的对象
        self.object_manager.mc:add_object(DISK_ARRAY_NAME, obj, controller_id)
        self:init_update_drive_list(controller_id, array_id)
    end)
    self.on_del_array:on(function(controller_id, array_id)
        local obj = self:get_ctrl_array_by_id(controller_id, array_id)
        if obj then
            -- 从资源树上删除对象
            local key = string.format('%d:%d', controller_id, array_id)
            class_mgmt(DISK_ARRAY_NAME):remove(self.array_list[key])
            -- 删除受模块管理的对象
            self.object_manager.mc:del_object(DISK_ARRAY_NAME, obj, controller_id)
        end
    end)
    self.on_controller_commu_changed:on(function(mctp_state, controller_id)
        if mctp_state then
            c_array.collection:fold(function(acc, obj, key)
                if obj.RefControllerId == controller_id then
                    obj:start()
                end
            end, {})
        else
            c_array.collection:fold(function(acc, obj, key)
                if obj.RefControllerId == controller_id then
                    obj:stop()
                end
            end, {})
        end
    end)
end

function c_array_collection:init_update_drive_list(controller_id, array_id)
    local array_obj = self:get_ctrl_array_by_id(controller_id, array_id)
    array_obj.on_update_ref_drives:on(function(obj)
        self.on_update_drive_list:emit(obj)
    end)
end

-- 以控制器Id为下标，注意不能多线程访问
local array_list = {}

function c_array_collection:get_ctrl_array_list_by_ctrl_id(controller_id)
    if array_list[controller_id] == nil then
        array_list[controller_id] = {}
    else
        for k, _ in pairs(array_list[controller_id]) do
            array_list[controller_id][k] = nil
        end
    end

    c_array.collection:fold(function(acc, obj, key)
        if obj.RefControllerId == controller_id then
            array_list[controller_id][obj.Id] = true
        end
    end, {})
    return array_list[controller_id]
end

function c_array_collection:get_ctrl_array_by_id(controller_id, array_id)
    return c_array.collection:find({ RefControllerId = controller_id, Id = array_id })
end

-- 根据diskname获取对应的array对象
function c_array_collection:get_ctrl_array_by_diskname(diskname)
    local array_id = nil
    local controller_id = nil
    c_array.collection:fold(function(acc, obj, key)
        for _, name in pairs(obj.RefDrives) do
            if name == diskname then
                array_id = obj.Id
                controller_id = obj.RefControllerId
                return nil
            end
        end
    end, {})
    if array_id and controller_id then
        return self:get_ctrl_array_by_id(controller_id, array_id)
    end
    return nil
end

function c_array_collection:dump_info(fp_w, controller_id)
    if not next(self.array_list) then
        fp_w:write("Not found disk array.\n\n\n")
        return
    end

    local count = 0
    c_array.collection:fold(function(acc, obj, key)
        if obj.RefControllerId == controller_id then
            count = count + 1
            obj:dump_info(fp_w)
        end
    end, {})
    if count == 0 then
        fp_w:write("No disk array in this controller.\n")
    end
    fp_w:write("\n\n")
end

return singleton(c_array_collection)
